#include "DS18B20.h"

void DS18B20_DQ_HIGH(void) { GPIO_SetBits(DS18B20_PORT, DS18B20_DQ_PIN); }
void DS18B20_DQ_LOW(void) { GPIO_ResetBits(DS18B20_PORT, DS18B20_DQ_PIN); }

uint8_t DS18B20_ReadDQ(void)
{
    uint8_t BitVal;
    BitVal = GPIO_ReadInputDataBit(DS18B20_PORT, DS18B20_DQ_PIN);
    return BitVal;
}
void DS18B20_IO_IN(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
void DS18B20_IO_OUT(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
}
uint8_t DS18B20_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    if (DS18B20_Reset() == RESET)
    {
        DS18B20_SendByte(0xcc); // 跳过ROM
        DS18B20_SendByte(0x44); // 启动转换
        return 0;
    }
    else
    {
        return 1;
    }
}

/**
 * 0表示器件存在
 */
uint8_t DS18B20_Reset(void)
{
    uint8_t retry1 = 0;
    uint8_t retry2 = 0;
    DS18B20_IO_OUT();
    DS18B20_DQ_LOW();  // 主机拉低总线 发送复位脉冲
    delay_us(500);     // 主机将总线拉低至少480us
    DS18B20_DQ_HIGH(); // 释放总线，同时IO口产生的上升沿能被DS18B20所检测到
    delay_us(60);      // 等待15~60us
    DS18B20_IO_IN();
    // 若存在 DS18B20主动将总线拉低60-240us,之后从机将释放总线
    while (DS18B20_ReadDQ() && retry1 < 240) // 240us内检测到低电平
    {
        retry1++;
        delay_us(1);
    }
    if (retry1 >= 240)
    {
        return 1; // 超时
    }
    while ((!DS18B20_ReadDQ()) && retry2 < 240) // 240us内检测到高电平
    {
        retry2++;
        delay_us(1);
    }
    if (retry2 >= 240)
    {
        return 1; // 超时
    }
    return 0;
}
/**
 * @attention 从低位发送
 */
void DS18B20_SendByte(uint8_t dat)
{
    uint8_t i;
    DS18B20_IO_OUT();
    for (i = 0; i < 8; i++)
    {
        if ((dat & 0x01))
        {
            // 表示发送1:主机将总线拉低1—15us，然后释放总线，
            // 从机将在总线拉低30us后读取电平，整个时间片应大于60us
            DS18B20_DQ_LOW();
            delay_us(2);
            DS18B20_DQ_HIGH();
            delay_us(61);
        }
        else
        {
            // 表示发送0: 主机将总线拉低60—120us，然后释放总线，
            // 从机将在总线拉低30us后读取电平，整个时间片应大于60us
            DS18B20_DQ_LOW();
            delay_us(61);
            DS18B20_DQ_HIGH();
            delay_us(2);
        }
        dat = (dat >> 1);
        delay_us(2); // 每次完成一位的送都需要释放总线至少1us
    }
}
uint8_t DS18B20_ReceiveByte(void)
{
    uint8_t i = 0;
    uint8_t dat = 0x00;
    DS18B20_IO_IN();
    for (i = 0; i < 8; i++)
    {
        // 主机将总线拉低1—15us，然后释放总线;并在15us内读取总线电平(尽量贴近15us末尾)
        // 整个时间片应大于60us
        DS18B20_DQ_LOW();
        delay_us(2);
        DS18B20_DQ_HIGH();
        delay_us(12);
        dat |= ((DS18B20_ReadDQ() & 0x01) << i);
        delay_us(50);
    }
    return dat;
}

/**
 * @attention 12bit;上电85°C;经过750ms左右时间采集后才显示真实的温度
 */
float DS18B20_ReadTemperature(void)
{
    float temperature = 0.0;
    uint16_t t = 0x0000;
    uint8_t high = 0x00;
    uint8_t low = 0x00;
    if (DS18B20_Reset() == RESET)
    {
        DS18B20_SendByte(0xcc); // 跳过ROM
        DS18B20_SendByte(0x44); // 启动转换
        DS18B20_Reset();
        DS18B20_SendByte(0xcc);       // 跳过ROM
        DS18B20_SendByte(0xbe);       // 读
        low = DS18B20_ReceiveByte();  // 前4位和高字节的后三位组成整数部分;后四位为小数部分
        high = DS18B20_ReceiveByte(); // 前5位是符号标志位(0-正数 1-负数);后三位和低字节前4位的组成整数部分
        t = (((uint16_t)high) << 8) | low;
        if ((t & 0xF800) == 0xF800)
        {
            t = (~t) + 1;
            temperature = (t * (-0.0625));
        }
        else
        {
            temperature = t * 0.0625;
        }
    }
    return temperature;
}
